# -*- coding: utf-8 -*-
# FileName:     persistence_gui.py
# time:         23/2/1 001 下午 10:12
# Author:       Zhou Hang
# Description:  I don't want to write
import os
import sys
from pathlib import Path

from PyQt5.QtGui import QTextCursor, QIcon
from PyQt5.QtWidgets import QMainWindow, QMessageBox

from FileSystemSims.src.core.afs import AFSCore
from common.zerror import MyCoreError
from FileSystemSims.src.core.checksum import checksumCore
from FileSystemSims.src.core.ffs import FFSCore
from FileSystemSims.src.core.fsck import FSCKCore
from FileSystemSims.src.core.lfs import LFSCore
from FileSystemSims.src.core.ssd import SSDCore
from FileSystemSims.src.core.vsfs import VSFSCore
from FileSystemSims.src.ui.persistence_ui import Ui_persistence_win


class Persistence(QMainWindow, Ui_persistence_win):
    def __init__(self):
        super(Persistence, self).__init__()
        self.default_ffs_search = None
        self.core = None
        self.setupUi(self)
        self.tmp_file = '../output/tmp.txt'
        self.side_fold_state = False  # 没有折叠
        self.fixed_main_side_width = self.main_side_wgt.width()
        self.btn_wgt_width = self.btn_wgt.width()
        self.init_common()
        self.init_vsfs()
        self.init_ffs()
        self.init_fsck()
        self.init_lfs()
        self.init_ssd()
        self.init_checksum()
        self.init_afs()

    # START DEBUG
    # END DEBUG

    # START COMMON
    def init_common(self):
        self.setWindowIcon(QIcon('../resources/main_icon.png'))
        self.show_hide_btn.clicked.connect(self.show_hide)
        self.choose_wgt.clicked.connect(self.choose_simulator)

    def choose_simulator(self):
        self.main_wgt.setCurrentIndex(self.choose_wgt.currentRow())

    def show_hide(self):
        if self.side_fold_state:  # 如果折叠了
            self.main_side_wgt.show()
            self.show_hide_btn.setText('>')
            self.side_wgt.setFixedWidth(self.fixed_main_side_width + self.btn_wgt_width)
            self.side_fold_state = False
        else:
            self.main_side_wgt.hide()
            self.show_hide_btn.setText('<')
            self.side_wgt.setFixedWidth(self.btn_wgt_width)
            self.side_fold_state = True

    def run_core(self, Core, args=None):
        save_stdout = sys.stdout
        sys.stdout = open(self.tmp_file, "w", encoding='utf-8')
        try:
            if args is not None:
                self.core = Core(**args)
            else:
                self.core.show_args()  # 展示参数
        except MyCoreError as e:
            QMessageBox.warning(self, '警告', str(e))
            return
        finally:
            sys.stdout.close()
            sys.stdout = save_stdout

    def put_file_in_gui(self, to_display):
        result = Path(self.tmp_file)
        to_display.setText(result.read_text(encoding='utf-8'))
        to_display.moveCursor(QTextCursor.Start)

    # END COMMON

    # START VSFS
    def init_vsfs(self):
        self.vsfs_start_btn.clicked.connect(self.vsfs_start)

    def vsfs_start(self):
        args = {
            'seed': self.seed_sb.value(),
            'numInodes': self.numInodes_vsfs_sb.value(),
            'numData': self.numData_vsfs_sb.value(),
            'numRequests': self.numRequests_vsfs_sb.value(),
            'reverse': self.reverse_vsfs_cb.isChecked(),
            'printFinal': self.printFinal_vsfs_cb.isChecked(),
            'compute': self.compute_cb.isChecked()
        }
        try:
            self.run_core(VSFSCore, args)
            self.put_file_in_gui(self.vsfs_edit)
        except Exception as e:
            print(repr(e))

    # END VSFS

    # START FFS
    def init_ffs(self):
        self.default_ffs_search = '../input/'
        filenames = os.listdir(self.default_ffs_search)  # 只支持单层文件，并且请不要在下面添加文件夹
        self.input_file_ffs_comb.addItems(filenames)
        self.ffs_start_btn.clicked.connect(self.ffs_start)

    def ffs_start(self):
        args = {
            'seed': self.seed_sb.value(),
            'num_groups': self.num_groups_ffs_sb.value(),
            'datablocks_per_groups': self.datablocks_per_groups_ffs_sb.value(),
            'inodes_per_group': self.inodes_per_group_ffs_sb.value(),
            'large_file_exception': self.large_file_exception_ffs_sb.value(),
            'input_file': self.default_ffs_search + self.input_file_ffs_comb.currentText(),
            'spread_inodes': self.spread_inodes_ffs_cb.isChecked(),
            'spread_data': self.spread_data_ffs_cb.isChecked(),
            'allocate_faraway': self.allocate_faraway_ffs_sb.value(),
            'contig_allocation_policy': self.contig_allocation_policy_ffs_sb.value(),
            'show_spans': self.show_spans_ffs_cb.isChecked(),
            'show_symbol_map': self.show_symbol_map_ffs_cb.isChecked(),
            'show_block_addresses': self.show_block_addresses_ffs_cb.isChecked(),
            'do_per_file_stats': self.do_per_file_stats_ffs_cb.isChecked(),
            'show_file_ops': self.show_file_ops_ffs_cb.isChecked(),
            'compute': self.compute_cb.isChecked(),
        }
        try:
            self.run_core(FFSCore, args)
            self.put_file_in_gui(self.ffs_edit)
        except Exception as e:
            print(repr(e))

    # END FFS

    # START FSCK
    def init_fsck(self):
        self.fsck_start_btn.clicked.connect(self.fsck_start)

    def fsck_start(self):
        args = {
            'seed': self.seed_sb.value(),
            'seedCorrupt': self.seedCorrupt_fsck_sb.value(),
            'numInodes': self.numInodes_vsfs_sb.value(),
            'numData': self.numData_vsfs_sb.value(),
            'numRequests': self.numRequests_vsfs_sb.value(),
            'printFinal': self.printFinal_vsfs_cb.isChecked(),
            'whichCorrupt': self.whichCorrupt_fsck_sb.value(),
            'compute': self.compute_cb.isChecked(),
            'dontCorrupt': not self.dontCorrupt_fsck_cb.isChecked()
        }
        try:
            self.run_core(FSCKCore, args)
            self.put_file_in_gui(self.fsck_edit)
        except Exception as e:
            print(repr(e))

    # END FSCK

    # START LFS
    def init_lfs(self):
        self.allocation_policy_lfs_comb.addItem('顺序', 's')
        self.allocation_policy_lfs_comb.addItem('随机', 'r')
        self.lfs_start_btn.clicked.connect(self.lfs_start)
        self.createfile_hs.valueChanged.connect(self.createfile)
        self.writefile_hs.valueChanged.connect(self.writefile)
        self.createdir_hs.valueChanged.connect(self.createdir)
        self.rmfile_hs.valueChanged.connect(self.rmfile)
        self.linkfile_hs.valueChanged.connect(self.linkfile)
        self.sync_hs.valueChanged.connect(self.sync)

    def createfile(self):
        self.createfile_lbl.setText(f'创建文件  {self.createfile_hs.value()}%')

    def writefile(self):
        self.writefile_lbl.setText(f'写入文件  {self.writefile_hs.value()}%')

    def createdir(self):
        self.createdir_lbl.setText(f'创建目录  {self.createdir_hs.value()}%')

    def rmfile(self):
        self.rmfile_lbl.setText(f'删除文件  {self.rmfile_hs.value()}%')

    def linkfile(self):
        self.linkfile_lbl.setText(f'链接文件  {self.linkfile_hs.value()}%')

    def sync(self):
        self.sync_lbl.setText(f'同步系统  {self.sync_hs.value()}%')

    def get_percentages(self):
        c = self.createfile_hs.value()
        w = self.writefile_hs.value()
        d = self.createdir_hs.value()
        r = self.rmfile_hs.value()
        l = self.linkfile_hs.value()
        s = self.sync_hs.value()
        return f'c{c},w{w},d{d},r{r},l{l},s{s}'

    def lfs_start(self):
        args = {
            'seed': self.seed_sb.value(),
            'no_force': self.no_force_lfs_cb.isChecked(),
            'no_final': self.no_final_lfs_cb.isChecked(),
            'use_disk_cr': self.use_disk_cr_lfs_cb.isChecked(),
            'compute': self.compute_cb.isChecked(),
            'show_operations': self.show_operations_lfs_cb.isChecked(),
            'show_intermediate': self.show_intermediate_lfs_cb.isChecked(),
            'show_return_codes': self.show_return_codes_lfs_cb.isChecked(),
            'show_live_paths': self.show_live_paths_lfs_cb.isChecked(),
            'num_commands': self.num_commands_lfs_sb.value(),
            'percentages': self.get_percentages(),
            'allocation_policy': self.allocation_policy_lfs_comb.currentData(),
            'command_list': ''  # TODO
        }
        try:
            self.run_core(LFSCore, args)
            self.put_file_in_gui(self.lfs_edit)
        except Exception as e:
            print(repr(e))
    # END LFS

    # START SSD
    def init_ssd(self):
        self.ssd_start_btn.clicked.connect(self.ssd_start)
        self.read_fails_ssd_hs.valueChanged.connect(self.read_fails_hs_changed)

    def read_fails_hs_changed(self):
        self.read_fails_ssd_lbl.setText(f'写操作失败比例 {self.read_fails_ssd_hs.value()}%')

    def ssd_start(self):
        args = {
            'seed': self.seed_sb.value(),
            'num_cmds': self.num_cmds_ssd_sb.value(),
            'op_percentages': f'{self.read_op_ssd_sb.value()}/{self.write_op_ssd_sb.value()}/{self.trim_op_ssd_sb.value()}',
            'skew': '' if self.skew_ssd_cb.isChecked() else f'{self.skew_ssd_sb2.value()}/{self.skew_ssd_sb1.value()}',
            'skew_start': 0 if self.skew_ssd_cb.isChecked() else self.skew_start_ssd_sb.value(),
            'read_fails': self.read_fails_ssd_hs.value(),
            'cmd_list': self.cmd_list_ssd_lineedit.text(),  # TODO:此功能暂时这么做，不用表格了，太麻烦了
            'ssd_type': self.ssd_type_ssd_comb.currentText(),
            'logical_pages': self.logical_pages_ssd_sb.value(),
            'num_blocks': self.num_blocks_ssd_sb.value(),
            'pages_per_block': self.pages_per_block_ssd_sb.value(),
            'high_water_mark': self.high_water_mark_ssd_sb.value(),
            'low_water_mark': self.low_water_mark_ssd_sb.value(),
            'read_time': self.read_time_ssd_sb.value(),
            'program_time': self.program_time_ssd_sb.value(),
            'erase_time': self.erase_time_ssd_sb.value(),
            'show_gc': self.show_gc_ssd_cb.isChecked(),
            'show_state': self.show_state_ssd_cb.isChecked(),
            'show_cmds': self.show_cmds_ssd_cb.isChecked(),
            'quiz_cmds': self.quiz_cmds_ssd_cb.isChecked(),
            'show_stats': self.show_stats_ssd_cb.isChecked(),
            'compute': self.compute_cb.isChecked()
        }
        try:
            self.run_core(SSDCore, args)
            self.put_file_in_gui(self.ssd_edit)
            self.ssd_tab_widget.setCurrentIndex(1)
        except Exception as e:
            print(repr(e))
    # END SSD

    # START checksum
    def init_checksum(self):
        self.checksum_start_btn.clicked.connect(self.checksum_start)

    def checksum_start(self):
        args = {
            'seed': self.seed_sb.value(),
            'data_size': self.data_size_checksum_sb.value(),
            'data': self.data_checksum_le.text(),
            'compute': self.compute_cb.isChecked()
        }
        try:
            self.run_core(checksumCore, args)
            self.put_file_in_gui(self.checksum_edit)
        except Exception as e:
            print(repr(e))
    # END checksum

    # START AFS
    def init_afs(self):
        self.afs_start_btn.clicked.connect(self.afs_start)

    def afs_start(self):
        try:
            detail = int(self.detail_afs_le.text())
        except ValueError:
            self.detail_afs_le.setText('1')
            detail = 1
        args = {
            'seed': self.seed_sb.value(),
            'clients': self.clients_afs_sb.value(),
            'numsteps': self.numsteps_afs_sb.value(),
            'numfiles': self.numfiles_afs_sb.value(),
            'readratio': self.readratio_afs_hs.value(),
            'actions': self.actions_afs_le.text(),
            'schedule': self.schedule_afs_le.text(),
            'printstats': self.printstat_cb.isChecked(),
            'compute': self.compute_cb.isChecked(),
            'detail': detail
        }
        try:
            self.run_core(AFSCore, args)
            self.put_file_in_gui(self.afs_edit)
        except Exception as e:
            print(repr(e))
    # END AFS


def main():
    pass


if __name__ == "__main__":
    main()
